/**
 * \file character.hpp
 * \brief Curses++ character header file
 *
 * This file contains declarations for a %character class used to substitute for
 * the complex %curses system.
 *
 * \if LICENSE
 * Curses++ - Free C++ wrapper to the curses library.
 * Copyright (C) 2007 Sean Hunt
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 * \endif
 */

#ifndef CURSESPLUSPLUS_CHARACTER_HPP
#define CURSESPLUSPLUS_CHARACTER_HPP

#include <locale>
#include <string>
#include <utility>

namespace curses
{
	/// Character class for use with %curses
	/**
	 *  This class wraps all data for characters in %curses. This is notable for
	 *  including attribute and especially color data.
	 */
	class character
	{
		public:
			/// A color for a single character.
			/**
			 *  The definition of a color is quite complex. In %curses, there is
			 *  a system of color pairs. This is needlessly complex, and so the
			 *  character system provides a color wrapper. The color can be
			 *  either a foreground color or a background color. Bolding is
			 *  (still) controlled through the bold attribute.
			 */
			enum color
			{
				black,		///< Black, or dark grey when bolded.
				red,		///< Blood red, or bright red when bolded.
				yellow,		///< Brown, or yellow when bolded.
				green,		///< Green, or lime when bolded.
				cyan,		///< Teal, or cyan when bolded.
				blue,		///< Deep blue, or bright blue when bolded.
				magenta,	///< Purple, or magenta when bolded.
				white,		///< Light grey, or pure white when bolded.
			};

			/// Special character attributes.
			/**
			 *  These attributes are defined by %curses. Most of these are
			 *  really odd, and can't be relied upon for actual use. The ones
			 *  that have common functionality (or at least don't fail on all
			 *  systems) are blink, bold, reverse, underline.
			 *
			 *  There are a lot of enum values you don't need to care about -
			 *  they are there because a separate type is needed for the
			 *  operator overloads to work.
			 */
			enum attribute
			{
				/// No attribute
				no_attr = 0,
				/// Alternate character set (not very well-defined)
				alternate = 1 << 0,
				/// Blinking characters.
				blink = 1 << 1,
				/// A bolded %character (brighter color, usually)
				bold = 1 << 2,
				/// A dimmer %character (usually no effect)
				dim = 1 << 3,
				/// Invisible %character.
				invisible = 1 << 4,
				/// A protected %character (?)
				protect = 1 << 5,
				/// Inverted video.
				reverse = 1 << 6,
				/// Stands the %character out (often reversed video)
				standout = 1 << 7,
				/// A line placed under the %character.
				underline = 1 << 8,

				// The rest are dummy values so as to allow the enum to work with & and | and ^.
				#include <cursesplusplus/attribute_enum_values.hpp>
			};

			// These really don't need to be here, but it's a convenient place to put them,
			// and this way they are out of the way of ordinary lookup, as well as visible
			// to the declarations in this class (which need them).
			friend attribute operator & (attribute lhs, attribute rhs)
			{
				return attribute(lhs & rhs);
			}

			friend attribute operator | (attribute lhs, attribute rhs)
			{
				return attribute(lhs | rhs);
			}

			friend attribute operator ^ (attribute lhs, attribute rhs)
			{
				return attribute(lhs ^ rhs);
			}

			friend attribute operator ~ (attribute rhs)
			{
				return attribute(~rhs) & alternate_and_blink_and_bold_and_dim_and_invisible_and_protect_and_reverse_and_standout_and_underline;
			}

			friend attribute& operator |= (attribute& lhs, attribute rhs)
			{
				return lhs = lhs | rhs;
			}

			friend attribute& operator &= (attribute& lhs, attribute rhs)
			{
				return lhs = lhs & rhs;
			}

			friend attribute& operator ^= (attribute& lhs, attribute rhs)
			{
				return lhs = lhs ^rhs;
			}

		private:
			/// The character stored.
			char c;
			/// The foreground color.
			color fg;
			/// The background color.
			color bg;
			/// Attribute storage.
			attribute attr;

		public:
			/// Create a brand-new %character.
			character (char c = 0, color fg = white, color bg = black, attribute attr = no_attr)
				: c(c), fg(fg), bg(bg), attr(attr)
			{}

			/// Set only the %character value.
			character& set_char (char c)
			{
				this->c = c;
				return *this;
			}

			/// Set the foreground color.
			character& set_fg (color fg)
			{
				this->fg = fg;
				return *this;
			}

			/// Set the background color.
			character& set_bg (color bg)
			{
				this->bg = bg;
				return *this;
			}

			/// Set all the attributes.
			/**
			 *  This replaces ALL attributes in the class with those specified.
			 */
			character& set_attr (attribute attr)
			{
				this->attr = attr;
				return *this;
			}

			/// Add the specified attributes
			character& add_attr (attribute attr)
			{
				this->attr |= attr;
				return *this;
			}

			/// Remove the specified attributes
			character& remove_attr (const attribute & attr)
			{
				this->attr &= ~attr;
				return *this;
			}

			/// Flip certain attributes
			character& flip_attr (attribute& attr)
			{
				this->attr ^= attr;
				return *this;
			}

			/// Filter attributes
			/**
			 *  This will remove all attributes that are not specified in attr.
			 */
			character& filter_attr (attribute& attr)
			{
				this->attr &= attr;
				return *this;
			}

			/// Set the %character's values.
			character& set (char c, color fg, color bg, attribute attr)
			{
				return set_char(c).set_fg(fg).set_bg(bg).set_attr(attr);
			}

			/// Get the display character.
			char get_char () const
			{
				return c;
			}

			/// Get the foreground color.
			color get_fg () const
			{
				return fg;
			}

			/// Get the background color.
			color get_bg () const
			{
				return bg;
			}

			/// Get the list of attributes.
			attribute get_attr() const
			{
				return attr;
			}

			/// Determine whether a specific attribute is set.
			bool has_attr(attribute attr) const
			{
				return this->attr & attr;
			}

			/// A shorthand for set_char.
			character& operator = (char c)
			{
				this->set_char(c);
				return *this;
			}

			/// Equality comparison
			friend bool operator == (const character& lhs, const character& rhs)
			{
				return lhs.c == rhs.c && lhs.fg == rhs.fg && lhs.bg == rhs.bg && lhs.attr == rhs.attr;
			}

			/// Less-than comparison
			friend bool operator < (const character& lhs, const character& rhs)
			{
				return lhs.c < rhs.c || lhs.fg < rhs.fg || lhs.bg < rhs.bg || lhs.attr < rhs.attr;
			}

			/// Not-equal comparsion
			friend bool operator != (const character& lhs, const character& rhs)
			{
				return !(lhs == rhs);
			}

			/// Greater-than comparison
			friend bool operator > (const character& lhs, const character& rhs)
			{
				return !(lhs == rhs || lhs < rhs);
			}

			/// Less-than-or-equal-to comparison
			friend bool operator <= (const character& lhs, const character& rhs)
			{
				return lhs == rhs || lhs < rhs;
			}

			/// Greater-than-or-equal-to comparison
			friend bool operator >= (const character& lhs, const character& rhs)
			{
				return lhs == rhs || !(lhs < rhs);
			}

			/// Set the attributes.
			character& operator = (attribute attr)
			{
				this->attr = attr;
				return *this;
			}

			/// Add specified attributes.
			friend character& operator |= (character& c, attribute attr)
			{
				c.attr |= attr;
				return c;
			}

			/// Remove all attributes NOT specified.
			/**
			 *  \warning This is parallel to filter_attr, not remove_attr.
			 */
			friend character& operator &= (character& c, attribute attr)
			{
				c.attr &= attr;
				return c;
			}

			/// Flip all attributes specified.
			friend character& operator ^= (character& c, attribute attr)
			{
				c.attr ^= attr;
				return c;
			}


			/// Get a %character with certain attributes set.
			friend character operator | (character c, attribute attr)
			{
				return character(c.c, c.fg, c.bg, c.attr | attr);
			}

			/// Get a %character with certain attributes filtered.
			friend character operator & (character c, attribute attr)
			{
				return character(c.c, c.fg, c.bg, c.attr & attr);
			}

			/// Get a %character with certain attributes flipped.
			friend character operator ^ (character c, attribute attr)
			{
				return character(c.c, c.fg, c.bg, c.attr ^ attr);
			}


			/// Get a %character with certain attributes set.
			friend character operator | (char c, attribute attr)
			{
				return character(c, white, black, attr);
			}

			/// Get a %character with certain attributes filtered.
			friend character operator & (char c, attribute attr)
			{
				UNUSED_PARAM(attr);
				return character(c);
			}

			/// Get a %character with certain attributes flipped.
			friend character operator ^ (char c, attribute attr)
			{
				UNUSED_PARAM(attr);
				return character(c, white, black, no_attr);
			}

			/// Conversion to a single character.
			operator char& ()
			{
				return this->c;
			}

			operator const char& () const
			{
				return this->c;
			}

		private:
			/// Keeping track of code conversions!
			enum iostate
			{
				none,
				colors,
				attributes,
			};
			friend class std::char_traits<character>;
			friend class std::ctype<character>;
			friend class std::codecvt<character, char, iostate>;
			friend void prepare_locale();
	};
}

#endif
